#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <netdb.h>
#include <sys/epoll.h>
#include <sys/socket.h>
#include <fcntl.h>
#include <errno.h>
#include <signal.h>

#include "Server.h"
#include "SvrMgr.h"
#include "BufMgr.h"

#define MAXEVENTS 128
// 进程间通讯每包数据不超过8K
// 大于8K则分包处理
#define IPC_BUFFER_MAX 2048

// #define BLOCK_MAX_SIZE 1448
// #define BLOCK_MAX_SIZE 2048
// #define BLOCK_MAX_LEVEL 20

// 每个服务的IPC接收、Client接收发送共用内存64M
#define BUFFER_CHUNK_SIZE 67108864

CServer::CServer()
{
    m_iefd = -1;
    m_irspfd = -1;
    m_iwspfd = -1;
    m_bRunning = true;
    m_pEvnts = nullptr;

    m_pIpcRecv = nullptr;
    m_pIpcSend = nullptr;
    m_pBufMgr = nullptr;
}

CServer::~CServer()
{
    Destroy();
}

bool CServer::Init(int *fds)
{
    bool bRet;
    CBufMgr<CSpinLock> *pIpcBufMgr = CSvrMgr::GetInstance()->GetIpcBufMgr();

    if (nullptr == fds || nullptr == pIpcBufMgr)
        return false;

    m_irspfd = fds[0];
    m_iwspfd = fds[1];
    m_pBufMgr = CBufMgr<>::Create(nullptr, BUFFER_CHUNK_SIZE);

    if (nullptr == m_pBufMgr)
    {
        Destroy();
        return false;
    }

    if (!m_pBufMgr->Init())
    {
        Destroy();
        return false;
    }

    m_pIpcRecv = m_pBufMgr->NewBuffer();

    if (nullptr == m_pIpcRecv)
    {
        Destroy();
        return false;
    }

    m_pIpcSend = pIpcBufMgr->NewBuffer();

    if (nullptr == m_pIpcSend)
    {
        Destroy();
        return false;
    }

    if (!m_ipb.Init(IPC_BUFFER_MAX))
    {
        Destroy();
        return false;
    }

    // 创建epoll
    m_iefd = epoll_create1(0);

    if (-1 == m_iefd)
    {
        Destroy();
        return false;
    }

    bRet = SetNonBlock(m_irspfd);

    if (!bRet)
    {
        Destroy();
        return false;
    }

    bRet = UpdateEvents(m_irspfd, EPOLLIN | EPOLLET, EPOLL_CTL_ADD, &m_irspfd);

    if (!bRet)
    {
        Destroy();
        return false;
    }

    bRet = SetNonBlock(m_iwspfd);

    if (!bRet)
    {
        Destroy();
        return false;
    }

    bRet = UpdateEvents(m_iwspfd, EPOLLOUT | EPOLLET, EPOLL_CTL_ADD, &m_iwspfd);

    if (!bRet)
    {
        Destroy();
        return false;
    }

    m_pEvnts = (epoll_event *)calloc(MAXEVENTS, sizeof(epoll_event));

    if (nullptr == m_pEvnts)
    {
        Destroy();
        printf("Error (nullptr == m_pEvnts):\tfile=%s\tline=%d\n", __FILE__, __LINE__);
        return false;
    }

    return true;
}

bool CServer::Start()
{
    bool bRet;

    while (m_bRunning)
    {
        bRet = EventHandle();

        if (!bRet)
            break;
    }

    return true;
}

bool CServer::Stop()
{
    IpcCmdMsg msg;

    msg.len = sizeof(IpcCmd);
    msg.cmd = IpcCmd::Exit;
    IpcSend(&msg, sizeof(msg));

    return true;
}

bool CServer::Destroy()
{
    if (m_bRunning)
        RunStop();

    if (-1 != m_iefd)
    {
        close(m_iefd);
        m_iefd = -1;
    }

    if (-1 != m_iwspfd)
    {
        close(m_iwspfd);
        m_iwspfd = -1;
    }

    if (-1 != m_irspfd)
    {
        close(m_irspfd);
        m_irspfd = -1;
    }

    if (nullptr != m_pEvnts)
    {
        free(m_pEvnts);
        m_pEvnts = nullptr;
    }

    if (nullptr != m_pIpcRecv)
    {
        m_pBufMgr->DeleteBuffer(m_pIpcRecv);
        m_pIpcRecv = nullptr;
    }

    if (nullptr != m_pIpcSend)
    {
        CBufMgr<CSpinLock> *pIpcBufMgr = CSvrMgr::GetInstance()->GetIpcBufMgr();

        pIpcBufMgr->DeleteBuffer(m_pIpcSend);
        m_pIpcSend = nullptr;
    }

    m_ipb.Destroy();

    if (nullptr != m_pBufMgr)
    {
        m_pBufMgr->Destroy();
        m_pBufMgr = nullptr;
    }

    return true;
}

void CServer::RunStop()
{
    m_bRunning = false;

    if (-1 != m_irspfd)
        UpdateEvents(m_irspfd, 0, EPOLL_CTL_DEL, nullptr);

    if (-1 != m_iwspfd)
        UpdateEvents(m_iwspfd, 0, EPOLL_CTL_DEL, nullptr);
}

bool CServer::EventHandle()
{
    bool bRet = true;
    int iLoop;
    int iCount = epoll_wait(m_iefd, m_pEvnts, MAXEVENTS, -1);

    // 被中断
    if ((-1 == iCount) && (errno == EINTR))
    {
        // printf("Log:\t(errno == EINTR):\tfile=%s\tline=%d\n", __FILE__, __LINE__);
        return true;
    }

    for (iLoop = 0; iLoop < iCount; iLoop++)
    {
        epoll_event &ee = m_pEvnts[iLoop];

        if ((ee.events & EPOLLERR) || (ee.events & EPOLLHUP))
        {
            bRet = ErrorHandle(ee);

            if (!bRet)
            {
                printf("Error ErrorHandle:\tfile=%s\tline=%d\n", __FILE__, __LINE__);
                break;
            }
        }
        else if (ee.events & EPOLLIN)
        {
            bRet = InputHandle(ee);

            if (!bRet)
            {
                printf("Error InputHandle:\tfile=%s\tline=%d\n", __FILE__, __LINE__);
                break;
            }
        }
        else if (ee.events & EPOLLOUT)
        {
            bRet = OutputHandle(ee);

            if (!bRet)
            {
                printf("Error OutputHandle:\tfile=%s\tline=%d\n", __FILE__, __LINE__);
                break;
            }
        }
    }

    return bRet;
}

bool CServer::ErrorHandle(epoll_event &ee)
{
    if ((&m_irspfd == ee.data.ptr) || (&m_iwspfd == ee.data.ptr))
    {
        kill(0, SIGINT);
        return false;
    }

    return ErrorEvent(ee);
}

bool CServer::InputHandle(epoll_event &ee)
{
    bool bRet = false;

    if (&m_irspfd == ee.data.ptr)
        bRet = IpcRecvHandle();
    else
        bRet = InputEvent(ee);

    return bRet;
}

bool CServer::OutputHandle(epoll_event &ee)
{
    bool bRet = false;

    if (&m_iwspfd == ee.data.ptr)
        bRet = IpcSendHandle();
    else
        bRet = OutputEvent(ee);

    return bRet;
}

bool CServer::IpcRecvHandle()
{
    bool bRes = false;
    int iRecv = m_pIpcRecv->Recv(m_irspfd, nullptr);

    if (0 < iRecv)
    {
        do
        {
            bRes = IpcPkgParse();

            if (bRes)
            {
                bRes = IpcCmdHandle();
                m_ipb.Reset();
                bRes = (bRes ? (m_pIpcRecv->GetDataLength() > 0) : false);
            }
        } while (bRes);
    }

    return true;
}

bool CServer::IpcSendHandle()
{
    int iLen = m_pIpcSend->GetDataLength();

    if (0 < iLen)
        m_pIpcSend->Send(m_iwspfd, nullptr); // 返回值检测

    return true;
}

bool CServer::IpcCmdHandle()
{
    bool bRet = false;

    switch (m_ipb.cmdIpc)
    {
    case IpcCmd::Invalid:
        printf("Error: IpcCmdHandle - invalid command\n");
        break;

    case IpcCmd::Exit:
        RunStop();
        bRet = false;
        break;

    default:
        bRet = IpcCmdEvent(m_ipb);
        break;
    }

    return bRet;
}

// IPC数据格式：1、长度(类型：uint16_t，值为后面两项的总长)
// 2、IPC命令(类型：enum class:uint16_t) 3、数据内容(可无)
bool CServer::IpcPkgParse()
{
    int iRecvLen = 0;
    uint16_t nPkgLen = m_ipb.nPkgLen;

    if (0 == nPkgLen)
    {
        iRecvLen = m_pIpcRecv->GetDataLength();

        if (iRecvLen < (int)sizeof(uint16_t))
            return false;

        m_pIpcRecv->Read(&nPkgLen, sizeof(uint16_t));
        m_ipb.nPkgLen = nPkgLen;
    }

    if (0 < nPkgLen)
    {
        iRecvLen = m_pIpcRecv->GetDataLength();

        if (iRecvLen < nPkgLen)
            return false;

        // 读取命令
        IpcCmd cmdIpc = IpcCmd::Invalid;
        m_pIpcRecv->Read(&cmdIpc, sizeof(IpcCmd));
        m_ipb.cmdIpc = cmdIpc;
        // 设置纯数据长度
        nPkgLen -= sizeof(IpcCmd);
        m_ipb.nPkgLen = nPkgLen;

        if (0 < nPkgLen)
            m_pIpcRecv->Read(m_ipb.pBuffer, nPkgLen);
    }

    return true;
}

// iLen大于IPC_BUFFER_MAX则需分包,由调用者来执行
int CServer::IpcSend(void *pSrc, int iLen)
{
    if (m_ipb.nMaxLen < iLen)
        return -1;

    return m_pIpcSend->Send(m_iwspfd, nullptr, pSrc, iLen);
}

int CServer::BindAndListen(unsigned short nPort)
{
    struct addrinfo aiConfig;
    struct addrinfo *pResult = nullptr;
    struct addrinfo *pItem = nullptr;
    int iRetVal;
    int isfd = -1;

    memset(&aiConfig, 0, sizeof(addrinfo));
    aiConfig.ai_family = AF_UNSPEC;
    aiConfig.ai_socktype = SOCK_STREAM;
    aiConfig.ai_flags = AI_PASSIVE;

    char szBufferPort[24] = {0};
    sprintf(szBufferPort, "%d", nPort);
    iRetVal = getaddrinfo(nullptr, szBufferPort, &aiConfig, &pResult);

    if (0 != iRetVal)
    {
        printf("Error getaddrinfo:%s\tfile=%s\tline=%d\n", gai_strerror(iRetVal), __FILE__, __LINE__);
        return -1;
    }

    for (pItem = pResult; pItem != nullptr; pItem = pItem->ai_next)
    {
        isfd = socket(pItem->ai_family, pItem->ai_socktype, pItem->ai_protocol);

        if (-1 == isfd)
            continue;

        iRetVal = bind(isfd, pItem->ai_addr, pItem->ai_addrlen);

        if (0 == iRetVal)
            break;

        close(isfd);
    }

    if (nullptr == pItem)
    {
        freeaddrinfo(pResult);
        printf("Error bind:\tfile=%s\tline=%d\n", __FILE__, __LINE__);
        return -1;
    }

    freeaddrinfo(pResult);

    if (!SetNonBlock(isfd))
    {
        close(isfd);
        printf("Error (-1 == iRetVal):\tfile=%s\tline=%d\n", __FILE__, __LINE__);
        return -1;
    }

    iRetVal = listen(isfd, SOMAXCONN);

    if (-1 == iRetVal)
    {
        close(isfd);
        printf("Error listen:\tfile=%s\tline=%d\n", __FILE__, __LINE__);
        return -1;
    }

    return isfd;
}

bool CServer::SetNonBlock(int ifd)
{
    int flags;
    int result;

    flags = fcntl(ifd, F_GETFL, 0);

    if (-1 == flags)
    {
        printf("Error fcntl F_GETFL:\tfile=%s\tline=%d\n", __FILE__, __LINE__);
        return false;
    }

    flags |= O_NONBLOCK;
    result = fcntl(ifd, F_SETFL, flags);

    if (-1 == result)
    {
        printf("Error fcntl F_SETFL:\tfile=%s\tline=%d\n", __FILE__, __LINE__);
        return false;
    }

    return true;
}

bool CServer::UpdateEvents(int ifd, int iEvnts, int iOpt, void *pData)
{
    bool bRet = true;
    struct epoll_event ev;

    if (-1 == m_iefd)
        return false;

    memset(&ev, 0, sizeof(ev));
    ev.events = iEvnts;
    ev.data.ptr = pData;
    int s = epoll_ctl(m_iefd, iOpt, ifd, &ev);

    if (-1 == s)
        bRet = false;

    return bRet;
}